package com.zhsecurity.customerfilter;

import com.zhsecurity.exception.VerificationCodeException;
import com.zhsecurity.service.VerificationCodeService;
import com.zhsecurity.service.impl.VerificationCodeServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.GenericFilterBean;

import javax.imageio.ImageIO;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 图形验证码过滤器
 * 特定的请求要求用户输入验证码
 */
public class VerificationCodeFilter extends GenericFilterBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(VerificationCodeFilter.class);

    private static final String SPRING_SECURITY_FORM_CODE_KEY = "code";
    private String codeParameter = SPRING_SECURITY_FORM_CODE_KEY;
    private AuthenticationFailureHandler authenticationFailureHandler;
    private VerificationCodeService verificationCodeService;
    private List<String> urls = new ArrayList<String>();
    private String imageUrl = "/imageCode";

    public VerificationCodeFilter(List<String> urls) {
        this.urls = urls;
        verificationCodeService = new VerificationCodeServiceImpl();
    }

    public VerificationCodeFilter(AuthenticationFailureHandler authenticationFailureHandler, List<String> urls) {
        verificationCodeService = new VerificationCodeServiceImpl();
        this.authenticationFailureHandler = authenticationFailureHandler;
        this.urls = urls;
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if (imageCodeRequest(request)) {
            BufferedImage image = verificationCodeService.createVerificationCode(request);
            // 输出图片
            response.setContentType("image/jpg");
            OutputStream outputStream = response.getOutputStream();
            ImageIO.write(image, "jpg", outputStream);
            outputStream.flush();
            outputStream.close();
            return;
        }

        if (!requiresAuthentication(request, response)) {
            filterChain.doFilter(servletRequest, servletResponse);
            System.out.println(">>>");
            //return;
        } else {
            String inputCode = request.getParameter(codeParameter);
            try {
                if (verificationCodeService.checkVerificationCode(request, inputCode)) {
                    filterChain.doFilter(servletRequest, servletResponse);
                }
            } catch (VerificationCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(request, response, e);
            }
        }
    }

    private boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        boolean match = false;
        for (String url : urls) {
            RequestMatcher requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(url);
            if (requiresAuthenticationRequestMatcher.matches(request)) {
                match = true;
                break;
            }
        }
        return match;
    }

    private boolean imageCodeRequest(HttpServletRequest request) {
        boolean match = false;
        RequestMatcher requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(imageUrl);
        if (requiresAuthenticationRequestMatcher.matches(request)) {
            match = true;
        }
        return match;
    }
}
